//let和const命令


{
    let a = 10;//let声明的变量只在它所在的代码块有效
    var b = 1;
}


var c = [];
for (let i = 0; i < 10; i++) {
    c[i] = function () {
        console.log(i);
    };
}
//输出c[6]()是6


console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError,let不像var那样会发生“变量提升”现象
var foo = 2;
let bar = 2;


var tmp = 123;
if (true) {
    tmp = 'abc'; // ReferenceError,存在全局变量tmp，但是块级作用域内let又声明了一个局部变量tmp，导致后者绑定这个块级作用域，所以在let声明变量前，对tmp赋值会报错
    let tmp;
}
//在代码块内，使用let命令声明变量之前，该变量都是不可用的。这在语法上，称为“暂时性死区”（temporal dead zone，简称TDZ）
//暂时性死区的本质就是，只要一进入当前作用域，所要使用的变量就已经存在了，但是不可获取，只有等到声明变量的那一行代码出现，才可以获取和使用该变量
if (true) {
    // TDZ开始
    tmp = 'abc'; // ReferenceError
    console.log(tmp); // ReferenceError

    let tmp; // TDZ结束
    console.log(tmp); // undefined

    tmp = 123;
    console.log(tmp); // 123
}


//不允许重复声明
// 报错
function aaa() {
    let a = 10;
    var a = 1;
}
// 报错
function bbb() {
    let a = 10;
    let a = 1;
}
function ccc(arg) {
    let arg; // 报错
}
function ddd(arg) {
    {
        let arg; // 不报错
    }
}


//块级作用域的出现，实际上使得获得广泛应用的立即执行匿名函数（IIFE）不再必要了
// IIFE写法
(function () {
    var tmp = 1;
}());
// 块级作用域写法
{
    let tmp = 1;
}

//const声明的变量不得改变值，这意味着，const一旦声明变量，就必须立即初始化，不能留到以后赋值
//const命令声明的常量也是不提升，和let一样，同样存在暂时性死区，只能在声明的位置后面使用
//不可变的只是这个地址，即不能把foo指向另一个地址，但对象本身是可变的，所以依然可以为其添加新属性
const foo = {};
foo.prop = 123;
foo.prop// 123
foo = {}; // TypeError: "foo" is read-only


//除了将对象本身冻结，对象的属性也应该冻结。下面是一个将对象彻底冻结的函数
var constantize = (obj) => {
    Object.freeze(obj);
    Object.keys(obj).forEach( (key, value) => {
        if ( typeof obj[key] === 'object' ) {
            constantize( obj[key] );
        }
    });
};


//全局变量a由var命令声明，所以它是全局对象的属性；全局变量b由let命令声明，所以它不是全局对象的属性，返回undefined
var a = 1;
// 如果在Node的REPL环境，可以写成global.a
// 或者采用通用方法，写成this.a
window.a // 1
let b = 1;
window.b // undefined